
// infdoc.cpp : implementation of the CinfDoc class
//

#include "stdafx.h"
#include "csvline.h"
#include "infdoc.h"

#include <propkey.h>
#include "exarchive.h"
#include <utility>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

static const unsigned BUF_SIZE(512); // size of buffer to test in text
static LPFILESEC g_pFileSect = NULL;
const LPFILESEC cmn::getFileSect()
{
	return g_pFileSect;
}

BOOL CinfDoc::DoPromptFileName(CString& fileName, UINT nIDFilter, UINT nIDSTitle)
{
	CString strFilter(L"All Files(*.*)|*.*||");
		strFilter.LoadString(nIDFilter);	//	AFX_IDS_ALLFILTER);
	//	
	CFileDialog dlgFile(TRUE, NULL, NULL, 
		OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, 
		strFilter, NULL, 0);
//		L"Info Files(*.inf)|*.inf|Text Files(*.inf;*.txt;*.ini)|*.inf;*.txt;*.ini|All Files(*.*)|*.*||", NULL, 0);

	CString title(L"Open");
		if (nIDSTitle) title.LoadString(nIDSTitle);
//	dlgFile.m_ofn.lpstrFilter = strFilter;
	dlgFile.m_ofn.lpstrTitle = title;
	dlgFile.m_ofn.lpstrFile = fileName.GetBuffer(_MAX_PATH);

		INT_PTR nResult = dlgFile.DoModal();

	fileName.ReleaseBuffer();
	return (nResult == IDOK);
}

// CinfDoc

IMPLEMENT_DYNCREATE(CinfDoc, CDocument)

BEGIN_MESSAGE_MAP(CinfDoc, CDocument)
	ON_COMMAND_RANGE(ID_FILE_NEW, ID_FILE_UPDATE, CinfDoc::OnFileCmds)
	ON_UPDATE_COMMAND_UI(ID_FILE_SAVE,		CinfDoc::OnUpdateFileSave)
	ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_AS,	CinfDoc::OnUpdateFileSaveAs)
END_MESSAGE_MAP()

// CinfDoc construction/destruction

CinfDoc::CinfDoc(): m_nIndex(0) { g_pFileSect = & m_proFile; }

const LPCSVLINE CinfDoc::getLine(int anindex) 
{ 
	POSITION thepos = m_lstTable.FindIndex(anindex);
	if (thepos) return & (m_lstTable.GetAt(thepos));
	return NULL;
}

BOOL CinfDoc::OnNewDocument()
{
	if (!CDocument::OnNewDocument())
		return FALSE;

	TFILESEC::Init();

	// TODO: add reinitialization code here
	m_proFile = TFILESEC();
	HWND thehwnd = AfxGetMainWnd()->operator HWND();
	if (thehwnd) ::PostMessage(thehwnd, WM_COMMAND, ID_VIEW_SMALLICON, 0);

	return TRUE;
}

void CinfDoc::DeleteContents() // delete doc items etc
{
	m_nIndex = 0;	//	clear content
	m_lstTable.RemoveAll();
}

BOOL CALLBACK CinfDoc::procEnumChild(HWND ahwnd, LPARAM lParam)
{
	long nWnd = ::GetWindowLong(ahwnd, GWL_ID);
	//	value of of splitter's client's more than hundred
	if (++nWnd >> 4) ::PostMessage(ahwnd, WM_DDE_TERMINATE, 0, lParam);
//		::PostMessage(ahwnd, AFX_WM_POSTSETPREVIEWFRAME, 0, pPair->second);
	return TRUE;
}

int CinfDoc::pasteNextLine(HWND ahWnd)
{
	int thecount = getCount();
	if (thecount)
	if (m_nIndex < thecount)
	{
		if (::EnumChildWindows(ahWnd, procEnumChild, m_nIndex)) 
			return ++m_nIndex;
//		::PostMessage(m_hViewPane, WM_COMMAND, ID_EDIT_PASTE, m_nIndex);
	}	//	that is all
		else m_nIndex = thecount + 1;

	return 0;
}

bool CinfDoc::LoadFileA(CArchive& ar)
{
	CFile * pFile = ar.GetFile();
	{
		BYTE thebuf[4];	//	test ansi
			* thebuf = 0;
			pFile->Read(thebuf, 4);
		WCHAR * firstbom = (WCHAR *) thebuf;
		if ((* firstbom == 0xFEFF) || (* firstbom == 0xFFFE))
			return false;

		if (!(thebuf[0]) || !(thebuf[1])) return false;
		if (!(thebuf[2]) || !(thebuf[3])) return false;

		int thepos(0); 	//	UTF8 BOM : 0xEF 0xBB 0xBF
		if (thebuf[0] == 0xEF) 
		{
			++thepos;
			if (thebuf[1] == 0xBB) 
			{
				++thepos;
				if (thebuf[2] == 0xBF) ++thepos;
			}
			pFile->Seek(thepos, CFile::begin);
			m_proFile.m_cBOM = * firstbom;
		}
			else pFile->SeekToBegin();
	}	//	test ansi

	CStringW thesec, theline, firstline;
	CStringA thestrA;
	bool bzero(false);
	BOOL retval = exar::ReadStringA(ar, thestrA, & bzero);
	if (retval && thestrA.GetLength())
	{
		if (exar::MultiByte2WideStr(thestrA, firstline))
				theline = firstline;
			else bzero = true;
	}

	if (bzero) return false; //	zero byte then binary or wide char

	unsigned count(0);
	if (retval)
	do {

		theline.Trim(TCSVLINE::szTrimChars);
			addNewLine(theline, thesec);
			++count;

		retval = exar::ReadStringA(ar, thestrA);
		if (!retval) break;

		if (thestrA.IsEmpty()) theline.Empty();
		else if (!exar::MultiByte2WideStr(thestrA, theline))
			 return false;

	} while (!(count >> 10));

	return true;
}

void CinfDoc::addNewLine(const CString & aline, CString & asect)
{
	if (aline.IsEmpty()) return;

	if (TCSVLINE::testSec(aline, asect))
	{
		asect.Trim(TCSVLINE::szTrimChars);
		asect.Trim(TCSVLINE::szQuoteApos);
	}
	else {
		TCSVLINE thecsv(asect);
		if (thecsv.loadAttr(aline))
			m_lstTable.AddTail(thecsv);
		else if (thecsv.loadTag(aline))
			m_lstTable.AddTail(thecsv);
	}

	return;
}

BOOL CinfDoc::OnSaveDocument(LPCTSTR ) 
{ 
	AfxMessageBox(IDS_FILE_SAVE, MB_OK | MB_ICONSTOP);
	return FALSE; 
}

BOOL CinfDoc::SaveModified() 
{
 // reset subs' for toggle of reg mode via ID_VIEW_LIST
	m_nIndex = 0;	
	SetModifiedFlag(FALSE);
	return TRUE; 
}

void CinfDoc::Serialize(CArchive& ar)	// CinfDoc serialization
{
	if (ar.IsStoring()) return;

	if (!(m_proFile.isUnicode()))
	{
		if(!(LoadFileA(ar))) 
			m_proFile.m_nState = 0;
		return;
	}

	WCHAR	firstbom(0);
		(ar.GetFile())->Read(& firstbom, sizeof(WCHAR));
	if ((firstbom == 0xFEFF) || (firstbom == 0xFFFE))
			m_proFile.m_cBOM = firstbom;
		else (ar.GetFile())->SeekToBegin(); // thebuf[thelen >> 1] = 0;

	if (!firstbom) 
	{
		m_proFile.m_nState = 0;
		return;
	}

	CString theline, thesec, firstline;
	BOOL retval = ar.ReadString(firstline); 
		theline = firstline;
	//	exar::ReadStringW(ar, firstline, & binary);
	unsigned count(0);
	if (retval)
	do {
		theline.Trim(TCSVLINE::szTrimChars);
			addNewLine(theline, thesec);
			++count;
//	AfxMessageBox(theline);
		retval = ar.ReadString(theline);
//		retval = exar::ReadStringW(ar, theline);
	} while(retval && !(count >> 10));

	return;
}

// CinfDoc diagnostics

#ifdef _DEBUG
void CinfDoc::AssertValid() const
{
	CDocument::AssertValid();
}

void CinfDoc::Dump(CDumpContext& dc) const
{
	CDocument::Dump(dc);
}
#endif //_DEBUG

// CinfDoc commands

void CinfDoc::OnFileCmds(UINT nCommandID)
{
	if (nCommandID == ID_FILE_NEW)
	{
		GetDocTemplate()->OpenDocumentFile(NULL);
		return;	//	blank
	}

	CString newName;
	if (nCommandID == ID_FILE_OPEN)
	{
		BOOL retval = DoPromptFileName(newName, IDS_OPEN_FILTER, AFX_IDS_OPENFILE);
		if (!retval || newName.IsEmpty()) return;	//	cancel
	}
		else newName = m_strPathName;

	WCHAR nBOM = 0;
	//	ID_FILE_UPDATE is inner msg
	if (nCommandID == ID_FILE_UPDATE) 
	{
		if (!IsModified()) return;
	}	//	above goodies, below error
	else if (!(testFile(newName, & nBOM))) return;
		

	//	synchronization: m_proFile.State and nCommandID (rendezvous)
	bool theunicode(true);
	switch (nCommandID)
	{
		case ID_FILE_OPEN : 
			if (!nBOM) 
			{
				AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_FILE_UPDATE);
				theunicode = false;
			}	//	else force unicode

		case ID_FILE_SAVE_AS : // UNICODE effort, locks next step
		case ID_FILE_UPDATE : 
			m_proFile.beforeOpen(theunicode);
		break;

		case ID_FILE_SAVE :	//	regular reload
			theunicode = false;
			if (m_lstTable.IsEmpty()) m_proFile.beforeOpen(theunicode);
			AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_FILE_UPDATE);
			if (nBOM || m_proFile.isUnicode()) SetModifiedFlag(TRUE);
				else break;	//	continue

		default: return; 
	}

//		pDoc = GetDocTemplate()->OpenDocumentFile(newName, bAddToMRU, TRUE);
	BOOL bAddToMRU = (nCommandID == ID_FILE_OPEN);
	CDocument * pDoc;
		pDoc = GetDocTemplate()->OpenDocumentFile(newName, bAddToMRU, TRUE);

	if (!pDoc || !(m_proFile.m_nState))		//	there is failed
		if (theunicode) //theApp.DelRecentFile(newName);	
				AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_FILE_NEW);
		//	above blank, below open unicode pass
			else SetModifiedFlag(TRUE);

	return;
}

bool CinfDoc::OnOpenRecentFile(LPCWSTR newName, BOOL bAddToMRU)
{
	if (!(testFile(newName))) return false;

	m_proFile.beforeOpen(false);	//m_proFile = TFILESEC();
	if (GetDocTemplate()->OpenDocumentFile(newName, bAddToMRU, TRUE))
		if (m_proFile.m_nState)
			return true;
	//	got doc't, next unicode case
	AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_FILE_UPDATE);
	SetModifiedFlag(TRUE);
	return true;
}

void CinfDoc::OnUpdateFileSave(CCmdUI *pCmdUI)
{
	//(pDoc) filepath = pDoc->GetPathName();
	pCmdUI->Enable(m_proFile.isFile());
}

void CinfDoc::OnUpdateFileSaveAs(CCmdUI *pCmdUI)
{
	// TODO: Add your command update UI handler code here
	pCmdUI->Enable(m_proFile.isFile());
}

bool CinfDoc::testFile(const CString & afilepath, WCHAR * pBom)
{
	WCHAR	thebuf[BUF_SIZE + 1];
		* thebuf = 0;

	UINT thelen(0), theerr(0);
	CFile	thefile;
	if (thefile.Open(afilepath, 0))
	{
		std::pair<DWORD, DWORD> thepair(0, 0);
		thepair.first = ::GetFileSize(thefile.operator HANDLE(), & (thepair.second));

		if ((thepair.first == INVALID_FILE_SIZE) || thepair.second)
			theerr = IDS_ERR_FILE_TOO_BIG;
		else	// BOM and BOM
			if (!(thepair.first >> 2)) theerr = IDS_ERR_FILE_EMPTY;
		m_proFile.m_nSize = thepair.first;

		if(!theerr) {
			thelen = thefile.Read(thebuf, BUF_SIZE * sizeof(WCHAR));
			thelen /= sizeof(WCHAR);
			if (thelen >> 1) 
			{
				if (thelen > BUF_SIZE) thelen = BUF_SIZE;
				thebuf[thelen] = 0;
			}
				else theerr = IDS_ERR_FILE_EMPTY;
		}
		thefile.Close();

	}
		else theerr = IDS_ERR_FILE_NOT_OPEN;

	if (theerr) 
	{
		CString thestr;
			thestr.Format(theerr, afilepath);
		AfxMessageBox(thestr, MB_OK | MB_ICONSTOP);
		return false;
	}

	if (::wcslen(thebuf) < (--thelen)) thelen = 0;
	else
	if (((* thebuf) == 0xFEFF) || ((* thebuf) == 0xFFFE))
	{
		if (pBom) * pBom = * thebuf;
	}
	else
		if(!(::wcschr(thebuf, L'\n')))
		if(!(::strchr((LPCSTR) thebuf, '\n'))) 
			thelen = 0;	//	CR is not found

	if (thelen) return true;

	CString thestr;
		thestr.Format(IDS_ERR_FILE_BIN, afilepath);
	AfxMessageBox(thestr, MB_OK | MB_ICONSTOP);
	return false;

}
